package com.ke.bella.openapi.protocol.completion;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Nullable;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.List;
import java.util.UUID;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@JsonInclude(Include.NON_NULL)
public class Message {

    // one of the system, user, assistant, function, tool
    @NotNull
    private String role;
    /**
     * content is required for all messages except assistant messages with
     * function calls; string or array<br/>
     * <br/>
     * for user role message, the
     * content can be array:<br/>
     * <br/>
     * [ { "type": "text", "text": "What’s in this image?" }, { "type":
     * "image_url", "image_url": { "url":
     * "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
     * } } ]
     */
    @Nullable
    private Object content;

    @JsonInclude(Include.NON_NULL)
    @Nullable
    private String reasoning_content;
    @JsonInclude(Include.NON_NULL)
    @Nullable
    private String redacted_reasoning_content;
    @JsonInclude(Include.NON_NULL)
    @Nullable
    private String reasoning_content_signature;

    /**
     * name is required if role is function, and it should be the name of the
     * function whose response is in the content;
     */
    @Nullable
    private String name;
    /**
     * Deprecated and replaced by tool_calls. The name and arguments of a
     * function that should be called, as generated by the model.
     */
    @JsonInclude(Include.NON_NULL)
    @Nullable
    private FunctionCall function_call;
    @JsonInclude(Include.NON_NULL)
    private List<ToolCall> tool_calls;
    /**
     * Tool call that this message is responding to.
     */
    private String tool_call_id;

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class ToolCall {

        private int index;

        /**
         * The ID of the tool call.
         */
        private String id;

        /**
         * The type of the tool. Currently, only function is supported.
         */
        private String type;

        /**
         * The function that the model called.
         */
        private FunctionCall function;

        @JsonInclude(Include.NON_NULL)
        private Object cache_control;

        @JsonProperty("id")
        public String getId() {
            return StringUtils.isBlank(id) ? "" : id;
        }

        public static ToolCall fromFunctionName(String name, int index) {
            return ToolCall.builder()
                    .id(UUID.randomUUID().toString())
                    .index(index)
                    .type("function")
                    .function(FunctionCall.builder().name(name).arguments("").build())
                    .build();
        }

        public static ToolCall fromFunctionIdAndName(String id, String name) {
            return ToolCall.builder()
                    .id(id)
                    .type("function")
                    .function(FunctionCall.builder().name(name).arguments("").build())
                    .build();
        }
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class Tool {
        private String type;
        private Function function;
        private Object cache_control;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class Function {

        /**
         * The name of the function to be called. Must be a-z, A-Z, 0-9, or
         * contain underscores and dashes, with a maximum length of 64.
         */
        @NotNull
        @Size(max = 64)
        private String name;
        /**
         * The description of what the function does.
         */
        @Nullable
        private String description;
        /**
         * The parameters the functions accepts, described as a JSON Schema
         * objec
         */
        @Nullable
        private FunctionParameter parameters;

        /**
         * JSON Schema 结构 example: { "type": "object", "properties": {
         * "location": { "type": "string", "description": "The city and state,
         * e.g. San
         * Francisco, CA", }, "unit": {"type": "string", "enum": ["celsius",
         * "fahrenheit"]}, }, "required": ["location"], }
         */

        @Data
        @AllArgsConstructor
        @NoArgsConstructor
        @Builder
        @JsonInclude(Include.NON_NULL)
        public static class FunctionParameter {
            private String type;
            private List<String> required;
            private Object properties;
            private Boolean additionalProperties;
        }
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class FunctionCall {

        /**
         * The name of the function to call.
         */
        @JsonInclude(Include.NON_NULL)
        private String name;
        /**
         * The arguments to call the function with, as generated by the model in
         * JSON format. Note that the model does not always generate valid JSON,
         * and may hallucinate parameters not defined by your function schema.
         * Validate the arguments in your code before calling your function.
         */
        private String arguments = "";

        public void appendArguments(String delta) {
            this.arguments += delta;
        }
    }

}
